/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef HMFS_DEFINE_H
#define HMFS_DEFINE_H

#include <inttypes.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>

#include "config.h"

#if defined(HAVE_LINUX_BLKZONED_H)
#include <linux/blkzoned.h>
#elif defined(HAVE_KERNEL_UAPI_LINUX_BLKZONED_H)
#include <kernel/uapi/linux/blkzoned.h>
#endif

namespace OHOS {
namespace Hmfs {
/*
 * Swap bits
 */
template <typename T>
constexpr T SwapBits(T value, T mask, uint32_t offset)
{
    return ((value >> offset) & mask) | ((value & mask) << offset);
}

inline uint16_t ReverseBytes(uint16_t value)
{
    constexpr uint32_t OFFSET_0 = 8;
    return static_cast<uint16_t>(value << OFFSET_0) | static_cast<uint16_t>(value >> OFFSET_0);
}

inline uint32_t ReverseBytes(uint32_t value)
{
    constexpr uint32_t BYTES_MASK = 0xff00ffU;
    constexpr uint32_t OFFSET_0 = 8;
    constexpr uint32_t OFFSET_1 = 16;
    value = SwapBits(value, BYTES_MASK, OFFSET_0);
    return (value >> OFFSET_1) | (value << OFFSET_1);
}

inline uint64_t ReverseBytes(uint64_t value)
{
    constexpr uint64_t BYTES_MASK = 0xff00ff00ff00ffLU;
    constexpr uint64_t WORDS_MASK = 0xffff0000ffffLU;
    constexpr uint32_t OFFSET_0 = 8;
    constexpr uint32_t OFFSET_1 = 16;
    constexpr uint32_t OFFSET_2 = 32;
    value = SwapBits(value, BYTES_MASK, OFFSET_0);
    value = SwapBits(value, WORDS_MASK, OFFSET_1);
    return (value >> OFFSET_2) | (value << OFFSET_2);
}

template <typename T>
constexpr T BSWAP(T x)
{
    if (sizeof(T) == sizeof(uint16_t)) {
        return ReverseBytes(static_cast<uint16_t>(x));
    }
    if (sizeof(T) == sizeof(uint32_t)) {
        return ReverseBytes(static_cast<uint32_t>(x));
    }
    return ReverseBytes(static_cast<uint64_t>(x));
}

#if __BYTE_ORDER == __BIG_ENDIAN
#define NATIVE_TO_LE16(x)        BSWAP(uint16_t(x))
#define NATIVE_TO_LE32(x)        BSWAP(uint32_t(x))
#define NATIVE_TO_LE64(x)        BSWAP(uint64_t(x))
#define LE16_TO_NATIVE(x)        BSWAP(uint16_t(x))
#define LE32_TO_NATIVE(x)        BSWAP(uint32_t(x))
#define LE64_TO_NATIVE(x)        BSWAP(uint64_t(x))
#elif __BYTE_ORDER == __LITTLE_ENDIAN
#define NATIVE_TO_LE16(x)        (static_cast<uint16_t>(x))
#define NATIVE_TO_LE32(x)        (static_cast<uint32_t>(x))
#define NATIVE_TO_LE64(x)        (static_cast<uint64_t>(x))
#define LE16_TO_NATIVE(x)        (static_cast<uint16_t>(x))
#define LE32_TO_NATIVE(x)        (static_cast<uint32_t>(x))
#define LE64_TO_NATIVE(x)        (static_cast<uint64_t>(x))
#endif

template <typename T, typename V>
inline void SetLeValue(T& member, V val)
{
    if constexpr (sizeof(T) == sizeof(uint16_t)) {
        member = NATIVE_TO_LE16(val);
    } else if constexpr (sizeof(T) == sizeof(uint32_t)) {
        member = NATIVE_TO_LE32(val);
    } else if constexpr (sizeof(T) == sizeof(uint64_t)) {
        member = NATIVE_TO_LE64(val);
    } else {
        member = val;
    }
}

template <typename T>
inline T GetLeValue(T value)
{
    if constexpr (sizeof(T) == sizeof(uint16_t)) {
        return LE16_TO_NATIVE(value);
    } else if constexpr (sizeof(T) == sizeof(uint32_t)) {
        return LE32_TO_NATIVE(value);
    } else if constexpr (sizeof(T) == sizeof(uint64_t)) {
        return LE64_TO_NATIVE(value);
    } else {
        return value;
    }
}

/*
 * Print to console
 */
template <typename T>
void PrintToConsole(const char* memberName, const T& member, bool layout)
{
    static_assert(std::is_integral<T>::value, "Type must be integral");
    T value = layout ? GetLeValue(member) : member;
    printf("%-35s ", memberName);
    if (layout) {
        if constexpr (sizeof(T) == sizeof(uint64_t)) {
            printf("%" PRIu64 "\n", value);
        } else if constexpr (sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint8_t)) {
            printf("%u\n", value);
        } else {
            printf("%u\n", value);
        }
    } else {
        if constexpr (sizeof(T) == sizeof(uint64_t)) {
            printf("\t[0x%16" PRIx64 " : %" PRIu64 "]\n", value, value);
        } else if constexpr (sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint8_t)) {
            printf("\t[0x%16x : %u]\n", value, value);
        } else {
            printf("\t[0x%16x : %u]\n", value, value);
        }
    }
}

#define PRINT_TO_COLSOLE(ptr, member) PrintToConsole(#member, (ptr)->member, g_hmfsConfig.layout)


/*
 * Debugging interfaces
 */
#define ASSERT(exp)                            \
    do {                                \
        if (!(exp)) {                        \
            printf("[ASSERT] (%s:%4d) %s\n", __func__, __LINE__, #exp);    \
            exit(-1);                    \
        }                            \
    } while (0)

#define MSG(n, fmt, ...)                        \
    do {                                \
        if (g_hmfsConfig.dbgLv >= n && !g_hmfsConfig.layout && !g_hmfsConfig.showFileMap) {    \
            printf(fmt, ##__VA_ARGS__);            \
        }                            \
    } while (0)

#define DBG(n, fmt, ...)                        \
    do {                                \
        if (g_hmfsConfig.dbgLv >= n && !g_hmfsConfig.layout && !g_hmfsConfig.showFileMap) {    \
            printf("[%s:%4d] " fmt, __func__, __LINE__, ##__VA_ARGS__);    \
        }                            \
    } while (0)

#define HMFS_ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))

#define HMFS_SUPER_MAGIC    0xF2F52010    /* F2FS Magic Number */
#define CP_CHKSUM_OFFSET    4092
#define SB_CHKSUM_OFFSET    3068
#define MAX_PATH_LEN        64
#define MAX_DEVICES         8

#define HMFS_BYTES_TO_BLK(bytes)    ((bytes) >> HMFS_BLKSIZE_BITS)
#define HMFS_BLKSIZE_BITS 12

/* for mkfs */
#define    DEFAULT_SECTORS_PER_BLOCK         8
#define    DEFAULT_BLOCKS_PER_SEGMENT        512

#define VERSION_LEN              256
#define VERSION_TIMESTAMP_LEN    4
#define VERSION_NAME_LEN         (VERSION_LEN - VERSION_TIMESTAMP_LEN)

enum HmfsConfigFunc {
    MKFS,
    FSCK,
    DUMP,
    DEFRAG,
    RESIZE,
    SLOAD,
    LABEL,
};

/*
 * code borrowed from kernel f2fs dirver: f2fs.h, GPL-2.0
 *  : definitions of COMPRESS_DATA_RESERVED_SIZE,
 *    struct CompressData, COMPRESS_HEADER_SIZE,
 *    and struct CompressCtx
 */
#define COMPRESS_DATA_RESERVED_SIZE        4
struct CompressData {
    uint32_t clen;            /* compressed data size */
    uint32_t chksum;            /* checksum of compressed data */
    uint32_t reserved[COMPRESS_DATA_RESERVED_SIZE];    /* reserved */
    uint8_t cdata[];            /* compressed data */
};

/* compress context */
struct CompressCtx {
    unsigned int clusterSize;    /* page count in cluster */
    unsigned int logClusterSize;    /* log of cluster size */
    void *rbuf;            /* compression input buffer */
    CompressData *cbuf;    /* comprsssion output header + data */
    size_t rlen;            /* valid data length in rbuf */
    size_t clen;            /* valid data length in cbuf */
    void *privateBuf;            /* work buf for compress algorithm */
};

struct StructDeviceInfo {
    char *path;
    int32_t fd;
    uint32_t sectorSize;
    uint64_t totalSectors;    /* got by get_device_info */
    uint64_t startBlkaddr;
    uint64_t endBlkaddr;
    uint32_t totalSegments;

    /* to handle zone block devices */
    int zonedModel;
    uint32_t nrZones;
    uint32_t nrRndZones;
    size_t zoneBlocks;
    uint64_t zoneSize;
    size_t *zoneCapBlocks;
};

struct DevCacheConfig {
    /* Value 0 means no cache, minimum 1024 */
    long numCacheEntry;

    /* Value 0 means always overwrite (no collision allowed). maximum 16 */
    unsigned maxHashCollision;

    bool dbgEn;
};

/* f2fs_configration for compression used for sload.f2fs */
struct compressOps {
    void (*init)(struct CompressCtx *cc);
    int (*compress)(struct CompressCtx *cc);
    void (*reset)(struct CompressCtx *cc);
};

/* Should be aligned to supported_comp_names and support_comp_ops */
enum CompressAlgorithms {
    COMPR_LZO,
    COMPR_LZ4,
    MAX_COMPRESS_ALGS,
};

enum FilterPolicy {
    COMPR_FILTER_UNASSIGNED = 0,
    COMPR_FILTER_ALLOW,
    COMPR_FILTER_DENY,
};

struct FilterOps {
    void (*add)(const char *);
    void (*destroy)(void);
    bool (*filter)(const char *);
};

struct CompressConfig {
    bool enabled;            /* disabled by default */
    bool required;            /* require to enable */
    bool readonly;            /* readonly to release blocks */
    CompressCtx cc;        /* work context */
    enum CompressAlgorithms alg;    /* algorithm to compress */
    compressOps *ops;        /* ops per algorithm */
    unsigned int minBlocks;    /* save more blocks than this */
    enum FilterPolicy filter;    /* filter to try compression */
    FilterOps *FilterOps;        /* filter ops */
};

struct hmfs_configuration {
    uint32_t reservedSegments;
    uint32_t newReservedSegments;
    int sparseMode;
    int zonedMode;
    int zonedModel;
    size_t zoneBlocks;
    double overprovision;
    double newOverprovision;
    uint32_t curSeg[6];
    uint32_t segsPerSec;
    uint32_t secsPerZone;
    uint32_t segsPerZone;
    uint32_t startSector;
    uint32_t totalSegments;
    uint32_t sectorSize;
    uint64_t deviceSize;
    uint64_t totalSectors;
    uint64_t wantedTotalSectors;
    uint64_t wantedSectorSize;
    uint64_t targetSectors;
    uint32_t sectorsPerBlk;
    uint32_t blksPerSeg;
    uint8_t initVersion[VERSION_LEN + 1];
    uint8_t sbVersion[VERSION_LEN + 1];
    uint8_t version[VERSION_LEN + 1];
    char *volLabel;
    char *volUuid;
    uint16_t encoding;
    uint16_t encodingFlags;
    int heap;
    int32_t kd;
    int32_t dumpFd;
    StructDeviceInfo devices[MAX_DEVICES];
    int ndevs;
    char *extensionList[2];
    const char *rootdevName;
    int dbgLv;
    int showDentry;
    int trim;
    int trimmed;
    int func;
    void *privateBuf;
    int dryRun;
    int noKernelCheck;
    int fixOn;
    int force;
    int defset;
    int bugOn;
    int forceStop;
    int abnormalStop;
    int fsErrors;
    int bugNatBits;
    bool quotaFixed;
    int allocFailed;
    int autoFix;
    int layout;
    int showFileMap;
    uint64_t showFileMapMaxOffset;
    int quotaFix;
    int preenMode;
    int ro;
    int preserveLimits;        /* preserve quota limits */
    int largeNatBitmap;
    int fixChksum;            /* fix old cp.chksum position */
    uint32_t feature;            /* defined features */
    unsigned int quotaBits;    /* quota bits */
    time_t fixedTime;

    /* mkfs parameters */
    int fakeSeed;
    uint32_t nextFreeNid;
    uint32_t quotaInum;
    uint32_t quotaDnum;
    uint32_t lpfInum;
    uint32_t lpfDnum;
    uint32_t lpfIno;
    uint32_t rootUid;
    uint32_t rootGid;

    /* defragmentation parameters */
    int defragShrink;
    uint64_t defragStart;
    uint64_t defragLen;
    uint64_t defragTarget;

    /* sload parameters */
    char *fromDir;
    char *mountPoint;
    char *targetOutDir;
    char *fsConfigFile;
    int preservePerms;

    /* resize parameters */
    int safeResize;

    /* precomputed fs UUID checksum for seeding other checksums */
    uint32_t chksumSeed;

    /* cache parameters */
    DevCacheConfig cacheConfig;

    /* compression support for sload.f2fs */
    CompressConfig compress;
};

extern hmfs_configuration g_hmfsConfig;

/*
 * Copied from fs/f2fs/f2fs.h
 */
#define    NR_CURSEG_DATA_TYPE    (3)
#define    NR_CURSEG_NODE_TYPE    (3)
#define    NR_CURSEG_TYPE    (NR_CURSEG_DATA_TYPE + NR_CURSEG_NODE_TYPE)

enum {
    CURSEG_HOT_DATA    = 0,    /* directory entry blocks */
    CURSEG_WARM_DATA,          /* data blocks */
    CURSEG_COLD_DATA,          /* multimedia or GCed data blocks */
    CURSEG_HOT_NODE,           /* direct node blocks of directory files */
    CURSEG_WARM_NODE,          /* direct node blocks of normal files */
    CURSEG_COLD_NODE,          /* indirect node blocks */
    NO_CHECK_TYPE
};

#define HMFS_MIN_SEGMENTS    9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */

/*
 * Copied from fs/f2fs/segment.h
 */
#define GET_SUM_TYPE(footer) ((footer)->entryType)
#define SET_SUM_TYPE(footer, type) ((footer)->entryType = type)

/*
 * Copied from include/linux/f2fs_sb.h
 */
#define HMFS_SUPER_OFFSET           1024    /* byte-size offset */
#define HMFS_MIN_LOG_SECTOR_SIZE    9    /* 9 bits for 512 bytes */
#define HMFS_MAX_LOG_SECTOR_SIZE    12    /* 12 bits for 4096 bytes */
#define HMFS_BLKSIZE                4096    /* support only 4KB block */
#define HMFS_MAX_EXTENSION          64    /* # of extension entries */
#define HMFS_EXTENSION_LEN          8    /* max size of extension */
#define HMFS_BLK_ALIGN(x)           (((x) + HMFS_BLKSIZE - 1) / HMFS_BLKSIZE)

#define NULL_ADDR        0x0U
#define NEW_ADDR         -1U
#define COMPRESS_ADDR    -2U

#define HMFS_MAX_QUOTAS        3

#define HMFS_ENC_UTF8_12_1         1

#define MAX_VOLUME_NAME        512

/* reason of stop_checkpoint */
enum StopCpReason {
    STOP_CP_REASON_SHUTDOWN,
    STOP_CP_REASON_FAULT_INJECT,
    STOP_CP_REASON_META_PAGE,
    STOP_CP_REASON_WRITE_FAIL,
    STOP_CP_REASON_CORRUPTED_SUMMARY,
    STOP_CP_REASON_UPDATE_INODE,
    STOP_CP_REASON_FLUSH_FAIL,
    STOP_CP_REASON_MAX,
};

#define MAX_HMFS_ERRORS            16

/*
 * For checkpoint
 */
#define CP_RESIZEFS_FLAG               0x00004000
#define CP_DISABLED_FLAG               0x00001000
#define CP_QUOTA_NEED_FSCK_FLAG        0x00000800
#define CP_LARGE_NAT_BITMAP_FLAG       0x00000400
#define CP_NOCRC_RECOVERY_FLAG         0x00000200
#define CP_TRIMMED_FLAG                0x00000100
#define CP_NAT_BITS_FLAG               0x00000080
#define CP_CRC_RECOVERY_FLAG           0x00000040
#define CP_FASTBOOT_FLAG               0x00000020
#define CP_FSCK_FLAG                   0x00000010
#define CP_ERROR_FLAG                  0x00000008
#define CP_COMPACT_SUM_FLAG            0x00000004
#define CP_ORPHAN_PRESENT_FLAG         0x00000002
#define CP_UMOUNT_FLAG                 0x00000001

#define HMFS_CP_PACKS        2    /* # of checkpoint packs */

#define CP_MIN_CHKSUM_OFFSET    CP_BITMAP_OFFSET

#define MIN_NAT_BITMAP_SIZE    64
#define MAX_SIT_BITMAP_SIZE_IN_CKPT    \
    (CP_CHKSUM_OFFSET - CP_BITMAP_OFFSET - MIN_NAT_BITMAP_SIZE)
#define MAX_BITMAP_SIZE_IN_CKPT    \
    (CP_CHKSUM_OFFSET - CP_BITMAP_OFFSET)


#define HMFS_INLINE_XATTR         0x01    /* file inline xattr flag */
#define HMFS_INLINE_DATA          0x02    /* file inline data flag */
#define HMFS_INLINE_DENTRY        0x04    /* file inline dentry flag */
#define HMFS_DATA_EXIST           0x08    /* file inline data exist flag */
#define HMFS_INLINE_DOTS          0x10    /* file having implicit dot dentries */
#define HMFS_EXTRA_ATTR           0x20    /* file having extra attribute */
#define HMFS_PIN_FILE             0x40    /* file should not be gced */
#define HMFS_COMPRESS_RELEASED    0x80    /* file released compressed blocks */

/*
 * For SIT entries
 *
 * Each segment is 2MB in size by default so that a bitmap for validity of
 * there-in blocks should occupy 64 bytes, 512 bits.
 * Not allow to change this.
 */
#define SIT_VBLOCK_MAP_SIZE 64
#define SIT_ENTRY_PER_BLOCK (HMFS_BLKSIZE / sizeof(struct sitEntry))

/*
 * F2FS uses 4 bytes to represent block address. As a result, supported size of
 * disk is 16 TB and it equals to 16 * 1024 * 1024 / 2 segments.
 */
#define SIZE_ALIGN(val, size)    (((val) + (size) - 1) / (size))
#define SEG_ALIGN(blks)        SIZE_ALIGN(blks, g_hmfsConfig.blksPerSeg)
#define ZONE_ALIGN(blks)    SIZE_ALIGN(blks, g_hmfsConfig.blksPerSeg * \
                    g_hmfsConfig.segsPerZone)

#define HMFS_MIN_SEGMENT      9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */
#define HMFS_MAX_SEGMENT_SIZE       ((16 * 1024 * 1024) / 2)
#define MAX_SIT_BITMAP_SIZE    (SEG_ALIGN(SIZE_ALIGN(HMFS_MAX_SEGMENT_SIZE, \
                        SIT_ENTRY_PER_BLOCK)) * \
                        g_hmfsConfig.blksPerSeg / 8)
#define MAX_CP_PAYLOAD         (SEG_ALIGN(SIZE_ALIGN(UINT32_MAX, NAT_ENTRY_PER_BLOCK)) * \
                        DEFAULT_NAT_ENTRY_RATIO / 100 * \
                        g_hmfsConfig.blksPerSeg / 8 + \
                        MAX_SIT_BITMAP_SIZE - MAX_BITMAP_SIZE_IN_CKPT)

/*
 * For segment summary
 *
 * One summary block contains exactly 512 summary entries, which represents
 * exactly 2MB segment by default. Not allow to change the basic units.
 *
 * NOTE: For initializing fields, you must use set_summary
 *
 * - If data page, nid represents dnode's nid
 * - If node page, nid represents the node page's nid.
 *
 * The ofsInNode is used by only data page. It represents offset
 * from node's page's beginning to get a data block address.
 * ex) data_blkaddr = (uint32_t)(nodepage_start_address + ofsInNode)
 */
#define    ENTRIES_IN_SUM         512
#define    SUMMARY_SIZE           (7)    /* sizeof(struct summary) */
#define    SUM_FOOTER_SIZE        (5)    /* sizeof(struct SummaryFooter) */
#define    SUM_ENTRIES_SIZE       (SUMMARY_SIZE * ENTRIES_IN_SUM)


#define SUM_JOURNAL_SIZE       (HMFS_BLKSIZE - SUM_FOOTER_SIZE - SUM_ENTRIES_SIZE)
#define NAT_JOURNAL_ENTRIES    ((SUM_JOURNAL_SIZE - 2) / sizeof(NatJournalEntry))
#define SIT_JOURNAL_ENTRIES    ((SUM_JOURNAL_SIZE - 2) / sizeof(SitJournalEntry))

} // namespace Hmfs
} // namespace OHOS

#endif // HMFS_DEFINE_H